package ObjectFile;

import Comp.Tokenize;

/**
 * The TextRecord class is responsible for creating the text records in the ending output file.
 * @author yogesh
 */
public class TextRecord {
    public static int location_count;   //0
    public static String debug_state;   //1
    public static String data;      //2
    public static String relocation_type;//3
    public static String s1_op;         //4
    public static String s1_sym;        //5
    public static String s2_op;         //6
    public static String s2_sym;        //7
    
    public static int operand_value;
    

    /**
     * Text Record Format
     * 0 - addr
     * 1 - debug
     * 2 - data
     * 3 - reloc
     * 4 - s1 op
     * 5 - s1 sym
     * 6 - s2 op
     * 7 - s2 sym
     * Changelog: made by Peter Saturday
     */
    public void createSP2TextRecords(){
        IceAssembly.Table table_instance = new IceAssembly.Table();
        IceAssembly.Convert convert_instance = new IceAssembly.Convert();
        
        for(int index=0;index<IceAssembly.Table.intermediate_row;index++){
            location_count = Integer.parseInt(IceAssembly.Table.intermediate_file[index][0],16);
            debug_state = IceAssembly.Table.intermediate_file[index][1];
            
            relocation_type = IceAssembly.Table.intermediate_file[index][4];
            s1_op = ""; s1_sym = "";
            s2_op = ""; s2_sym = "";
            operand_value = 0;
            
            
            if(! IceAssembly.Table.intermediate_file[index][3].contains("<") ){
                data = IceAssembly.Table.intermediate_file[index][2]+IceAssembly.Table.intermediate_file[index][3]; //5 bits code and 11 bits code
                data = combineBitsToHex(data, ""); //want four hex
            } else{
                // had <<someExpression>>
                data = IceAssembly.Table.intermediate_file[index][2]+"0";
                String[] split_expression = extractExpression(IceAssembly.Table.intermediate_file[index][3]);
                new TextRecord().processExpression(split_expression[1]);
                        
                if(operand_value < -1024 || operand_value > 1023){
                    System.out.println("Regarding location:"+location_count);
                    new IceAssembly.Initialize().printErrorMessage(161);
                    operand_value=0;
                }
                data = combineBitsToHex(data, convert_instance.getBitCode(String.valueOf(operand_value), 10)); //want four hex
            }    
            String location_string = new IceAssembly.Convert().intToFourHex(location_count);
            table_instance.addToTextRecord(location_string, debug_state, data, relocation_type, s1_op, s1_sym, s2_op, s2_sym);
        }
    }

    /**
    * This function will take two strings of binary and convert it to hex.
    * <p>
    * <b>Change Log:</b><br>
    * None<br>
    * <p>
    * <b>Error Conditions Tested:</b><br>
    * None <br>
    * <p>
    * <b>Date of module being installed:</b> 5/10/08
    * <p><b>Original Author:</b>  Keith Hill
    * @param first The first 5 bits
    * @param second The second 11 bits
    * @return The hexidecimal value is returned to the calling function.
    */
    public String combineBitsToHex(String first, String second){
        String binary = first+second; 
        IceAssembly.Convert convert_instance = new IceAssembly.Convert();
        return convert_instance.intToFourHex(Integer.parseInt(binary, 2));
     }

    /**
    * This function will take one function and return an array of functions.  It is called upon when 
         * there is a label involved in the intermediate file and it is indicated by a special character.  
         * The first element in the array is everything before the special character and the second item in 
         * the array is everything else (once the special characters are removed).
    * <p>
    * <b>Change Log:</b><br>
    * None<br>
    * <p>
    * <b>Error Conditions Tested:</b><br>
    * None <br>
    * <p>
    * <b>Date of module being installed:</b> 5/10/08
    * <p><b>Original Author:</b>  Keith Hill
    * @param mixed_expression The incoming expression.
    * @return The expression is broken into two parts and returned.  The special separator character denotes the breaking point.
    */
    public String [] extractExpression(String mixed_expression){
        String [] result = new String [2];
        
        if(mixed_expression.charAt(0) == '<'){
            mixed_expression = mixed_expression.replaceAll("<", "");
            mixed_expression = mixed_expression.replaceAll(">", "");
            result[0] = "";
            result[1] = mixed_expression;
        }
        else{
            int counter = 0;
            result[0] = "";
            while(mixed_expression.charAt(counter) != '<'){
                result[0] = result[0] + mixed_expression.charAt(counter);
                counter++;
            }
            mixed_expression = mixed_expression.replaceFirst(result[0], "");
            mixed_expression = mixed_expression.replaceAll("<", "");
            mixed_expression = mixed_expression.replaceAll(">", "");
            result[1] = mixed_expression;
        }
        return result;
    }
    
    /**
     * Expressions can contain:
     * local operator local
     * external operator external
     * external operator local
     * local operator external
     * local operator constant
     * constant operator local
     * external operator constant
     * constant operator external
     * 
     * where * is considered a constant
     * 
     * @param expression
     */
    private void processExpression(String expression){
        if(!(expression.charAt(0) == '+' || expression.charAt(0) == '+' )){
            expression="+"+expression;
        }
        Tokenize tokenize_instance = new Tokenize();
        String[] tokenized_expression = tokenize_instance.tokenizeComp(expression);
        tokenized_expression = new Comp.Evaluate().chopLastBlankIndexes(tokenized_expression);
        
        switch(tokenized_expression.length){
            case 2:
                new TextRecord().processExpressionLength2(tokenized_expression);
                break;
            case 4:
                new TextRecord().processExpressionLength4(tokenized_expression);
                break;
            default:
                System.out.println("ERROR: Strange form of text record <<expression>> found");
                break;
        }
    }
    
    /**
     * 0 - operator
     * 1 - local, external, star
     * @param tokenized_expression
     */
    private void processExpressionLength2(String[] tokenized_expression){
        IceAssembly.Table table_instance = new IceAssembly.Table();
        IceAssembly.Check check_instance = new IceAssembly.Check();
        IceAssembly.Convert convert_instance = new IceAssembly.Convert();
        
        if(tokenized_expression[1].contentEquals("*")){
            processOperandStar(tokenized_expression);
        } else {
            if(check_instance.isExternalLabel(tokenized_expression[1])== 1){
                processOperandExternal2(tokenized_expression);
            } else {
                if (! table_instance.getEquValueOfLabel(tokenized_expression[1]).contentEquals("-100")){
                    processOperandEquate2(tokenized_expression);
                } else if (Integer.parseInt(table_instance.getLcOfLabel(tokenized_expression[1]),16) >= 0){
                    processOperandLocal2(tokenized_expression);
                } else if (check_instance.isInt(tokenized_expression[1])==1){
                    processOperandConstant2(tokenized_expression);
                } else {
                    new IceAssembly.Initialize().printErrorMessage(167); //use defined label
                    operand_value = 0;
                }
            }
        }
    }
    
    private void processOperandConstant2(String[] tokenized_expression){
        if (tokenized_expression[0].contentEquals("+")){
            operand_value += Integer.valueOf(tokenized_expression[1]);
        } else {
            operand_value -= Integer.valueOf(tokenized_expression[1]);
        }
        if(relocation_type.contentEquals("unset")){
            relocation_type = "A";
        }
    }
    
    private void processOperandEquate2(String[] tokenized_expression){
        int equated_value = Integer.parseInt(new IceAssembly.Table().getEquValueOfLabel(tokenized_expression[1]),16);
        
        if(tokenized_expression[0].contentEquals("+")){
            operand_value += equated_value;
        } else {
            operand_value -= equated_value;
        }
        if(relocation_type.contentEquals("unset")){
            relocation_type = "A";
        }
    }
    
    /**
     * process Star Addressing
     * @param tokenized_expression
     */
     private void processOperandStar(String[] tokenized_expression){
        IceAssembly.Table table_instance = new IceAssembly.Table();
        s1_op = "+";
        s1_sym = table_instance.getAllLabels("START")[1];
        
        if(tokenized_expression[0].contentEquals("+")){
            operand_value += location_count;
            
            if(relocation_type.contentEquals("unset")){
                relocation_type = "R";
            } else {
                relocation_type = "C";
            }
            
        } else {
            operand_value -= location_count;
            
            if(relocation_type.contentEquals("unset")){
                relocation_type = "R";
            } else if(relocation_type.contentEquals("R")){
                relocation_type = "A";
                s1_op = ""; s1_sym=""; s2_op=""; s2_sym="";
            } else {
                relocation_type = "C";
            }
        }
    }
    
    private void processOperandExternal2(String[] tokenized_expression){
        
        if(s1_op.length()==0){
            s1_op = tokenized_expression[0];
            s1_sym = tokenized_expression[1];
        } else {
            s2_op = tokenized_expression[0];
            s2_sym = tokenized_expression[1];
        }
        if(relocation_type.contentEquals("unset")){
            relocation_type = "E";
        } else {
            relocation_type = "C";
        }
    }
    
    private void processOperandLocal2(String[] tokenized_expression){
        IceAssembly.Table table_instance = new IceAssembly.Table();
        if(s1_op.length()==0){
            s1_op = "+";
            s1_sym = table_instance.getAllLabels("START")[1];
        } else {
            s2_op = "+";
            s2_sym = table_instance.getAllLabels("START")[1];
        }
        
        
        int label_loc = Integer.parseInt(table_instance.getLcOfLabel(tokenized_expression[1]),16);
        if(tokenized_expression[0].contentEquals("+")){
            operand_value += label_loc;
            
            if(relocation_type.contentEquals("unset")){
                relocation_type = "R";
            }else if(relocation_type.contentEquals("RE")){
                relocation_type = "R";
            } else {
                relocation_type = "C";
            }
            
        } else {
            operand_value -= label_loc;
            
            if(relocation_type.contentEquals("unset")){
                relocation_type = "R";
            } else if(relocation_type.contentEquals("R")){
                relocation_type = "A";
                s1_op = ""; s1_sym=""; s2_op=""; s2_sym="";
            } else {
                relocation_type = "C";
            }
            
        }
    }
     
    /**
     * 0 - operator
     * 1 - local, external, star/constant
     * 2 - operator
     * 3 - local, external, star/constant
     * @param tokenized_expression
     */
    private void processExpressionLength4(String[] tokenized_expression){
        IceAssembly.Table table_instance = new IceAssembly.Table();
        IceAssembly.Check check_instance = new IceAssembly.Check();
        IceAssembly.Convert convert_instance = new IceAssembly.Convert();
        
        relocation_type = "unset";
        
        String[] half_expression = new String[2];
        half_expression[0] = tokenized_expression[0]; 
        half_expression[1] = tokenized_expression[1];
        
        processExpressionLength2(half_expression);
        
        half_expression[0] = tokenized_expression[2];
        half_expression[1] = tokenized_expression[3];
        processExpressionLength2(half_expression);
    }
    
    /**
     * Splits string into its operator and operand components
     * 
     * @param expression Externals expression of form +e1-e2 or related
     * @return String array with 4 elements. 0 and 2 are operators and 1 and 3 are external labels.
     */
    public String[] tokenizeExternals(String expression){
        String[] tokenized_string = new String[4];
        tokenized_string[0]="";tokenized_string[1]="";tokenized_string[2]="";tokenized_string[3]="";
        String terminals = "+-";
        int array_index = 0;
        int pos = 0;
        while(expression.length() > 0 && expression.contains("+") || expression.contains("-")){
            if(terminals.contains(String.valueOf(expression.charAt(pos)))){
                if(pos==0){
                    tokenized_string[array_index]=String.valueOf(expression.charAt(pos));
                    array_index++;
                    expression=expression.substring(1);
                } else {
                    tokenized_string[array_index]=expression.substring(0,pos);
                    array_index++;
                    tokenized_string[array_index]=String.valueOf(expression.charAt(pos));
                    array_index++;
                    expression=expression.substring(pos+1);
                }
                pos=0;
            }
        }
        return tokenized_string;
    }
    
 
}